package tum0r.webengine.models.utils

import com.alibaba.fastjson.JSON
import tum0r.webengine.config.BaseConfig
import tum0r.webengine.enums.EConfigType
import tum0r.webengine.extension.urlEncode
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.util.*
import kotlin.experimental.and

/**
 * 说明: CSV表格数据
 *
 * 创建者: tum0r
 *
 * 时间: 2021/8/29 14:15
 *
 * @param[name] CSV文件名称，默认为时间戳
 */
data class CSV constructor(var name: String = Date().time.toString()) {
    /**
     * 列名
     */
    private val columnName: ArrayList<String> = ArrayList()

    /**
     * 所有行
     */
    private val rows: ArrayList<ArrayList<String>> = ArrayList()

    /**
     * 每行数据
     */
    private lateinit var columns: ArrayList<String>

    /**
     * 新开启一行
     */
    private fun newColumns() {
        columns = ArrayList()
        rows.add(columns)
    }

    /**
     * 列名数
     */
    fun columnNameCount(): Int = columnName.size

    /**
     * 行数
     */
    fun rowCount(): Int = rows.size + if (rows.size > 0 && rows.last().size == 0) -1 else 0

    /**
     * 某一行的列数
     *
     * @param[index] 行下标
     */
    fun columnCount(index: Int): Int = if (index in 0 until rowCount()) rows[index].size else -1

    /**
     * 获取文件下载名
     */
    fun getFileDownloadName(): String = "${if (BaseConfig.isState(EConfigType.FILE_NAME_URL_ENCODE)) name.ifBlank { Date().time.toString() }.urlEncode() else name.ifBlank { Date().time.toString() }}.csv"

    init {
        newColumns()
    }

    /**
     * 插入一列的列名
     *
     * @param[index] 列名下标
     * @param[name] 列名
     */
    fun insertColumnName(index: Int, name: String) = columnName.add(index, name)

    /**
     * 在最后插入一列的列名
     *
     * @param[name] 列名
     */
    fun addColumnName(name: String) = insertColumnName(columnName.size, name)

    /**
     * 插入一列的值，自动换到下一行
     *
     * @param[index] 列下标
     * @param[value] 值
     */
    fun insertColumn(index: Int, value: String?) {
        val size = columnNameCount()
        if (!(size != 0 && index in 0..size)) return
        columns.add(value ?: "")
        if (columns.size == size) newColumns()
    }

    /**
     * 在当前已有的列最后插入一列的值，自动换到下一行
     *
     * @param[value] 值
     */
    fun addColumn(value: String?) = insertColumn(columns.size, value)

    /**
     * 插入列数据，自动换到下一行
     */
    fun addColumns(vararg values: String?) {
        for (value in values) addColumn(value)
    }

    /**
     * 插入一行数据
     */
    fun addRows(vararg values: String?) {
        if (values.isEmpty()) return
        if (columns.size != 0) newColumns()
        val temp = Array<String?>(columnNameCount()) { null }
        for (i in temp.indices) temp[i] = if (i in values.indices) values[i] else null
        addColumns(*temp)
    }

    /**
     * 根据强类型对象插入一行或多行数据
     */
    fun addRows(vararg objects: Any?) {
        if (objects.isEmpty() || columnNameCount() == 0) return
        for (obj in objects) {
            val json = if (obj != null) JSON.parseObject(JSON.toJSONString(obj)) else JSON.parseObject("{}")
            for (name in columnName) addColumn(json.getString(name))
        }
    }

    /**
     * 获取所有行数据
     *
     * @param[targetClass] 强类型的类
     */
    fun <T> getAllRows(targetClass: Class<T>): ArrayList<T> {
        val result = ArrayList<T>()
        for (i in 0 until rowCount()) result.add(JSON.parseObject(JSON.toJSONString(this.rows[i]), targetClass))
        return result
    }

    /**
     * 获取所有行数据
     */
    inline fun <reified T> getAllRows(): ArrayList<T> = getAllRows(T::class.java)

    /**
     * 获取一行数据
     *
     * @param[index] 行下标
     * @param[targetClass] 强类型的类
     */
    fun <T> getRow(index: Int, targetClass: Class<T>) = getAllRows(targetClass).getOrNull(index)

    /**
     * 获取一行数据
     *
     * @param[index] 行下标
     */
    inline fun <reified T> getRow(index: Int) = getRow(index, T::class.java)

    /**
     * 获取某一行某一列数据
     *
     * @param[rowIndex] 行下标
     * @param[columnIndex] 列下标
     *
     * @return 获取不到返回null
     */
    fun getColumn(rowIndex: Int, columnIndex: Int): String? {
        if (rowIndex !in 0 until rowCount()) return null
        if (columnIndex !in 0 until columnCount(rowIndex)) return ""
        return getRow<ArrayList<String>>(rowIndex)!![columnIndex]
    }

    /**
     * 获取某一行某一列数据
     *
     * @param[rowIndex] 行下标
     * @param[columnName] 列名
     *
     * @return 获取不到返回null
     */
    fun getColumn(rowIndex: Int, columnName: String): String? = getColumn(rowIndex, this.columnName.indexOf(columnName))


    /**
     * csv文件中，字段用双引号包起来，字符串里有双引号的需要在前面再加一个双引号
     */
    private fun format(text: String) = "\"${text.replace("\"", "\"\"")}\""

    /**
     * 获取结果字符串
     *
     * @param[charset] 字符串编码
     */
    @JvmOverloads
    fun getResultString(charset: Charset = StandardCharsets.UTF_8): String {
        val count = columnNameCount()
        if (count > 0) return ""
        val result = StringBuilder()
        for (name in columnName) result.append(format(name)).append(",")
        result.deleteAt(result.length - 1).appendLine()
        for (row in rows) {
            for (column in row) result.append(format(column))
            if (count != row.size) result.append("\"\",".repeat(row.size - count))
            result.deleteAt(result.length - 1).appendLine()
        }
        return String(result.toString().toByteArray(charset))
    }

    /**
     * 获取结果字符串的十六进制字符串
     *
     * @param[charset] 字符串编码
     */
    @JvmOverloads
    fun getResultHexString(charset: Charset = StandardCharsets.UTF_8): String {
        val resultString = getResultString(charset)
        if (resultString.isBlank()) return ""
        val result = StringBuilder()
        val data = resultString.toByteArray(charset)
        val b = 0xff.toByte()
        for (d in data) {
            val hex = (d and b).toString(16)
            if (hex.length == 1) result.append("0")
            result.append(hex)
        }
        return result.toString()
    }

    /**
     * 获取结果字符串的十六进制字符串的字节数组
     *
     * @param[charset] 字符串编码
     */
    @JvmOverloads
    fun getResultHexStringByteArray(charset: Charset = StandardCharsets.UTF_8): ByteArray {
        val hexString = getResultHexString(charset)
        if (hexString.isBlank()) return ByteArray(0)
        val result = ByteArray(hexString.length / 2)
        for (i in hexString.indices step 2) result[i / 2] = hexString.substring(i, i + 2).toByte(16)
        return result
    }
}